在開發 LLM(大型語言模型)應用的過程中,有沒有發現有時候執行的結果跟你預期的有點不一樣?你設計的 Prompt(提示語)或是 Plugin function(外卦功能)的調用,經常會跑出有點困惑的結果?這時候如果有一個有效的追蹤和管理工具,我們就能進行追蹤、記錄並改善。Semantic Kernel 框架當然也提供了這方面的機制,本篇內容就來說說如何偷偷觀察 Prompt 和 Plugin Function。
LLM 是個很聰明但又愛偷懶的小天才,也有點喜歡「自作主張」。就像一個廚師,你給了食材和菜單,他可能照SOP做,也可能按自己的風格來個「隨意加料」創造驚喜。對於 LLM 應用來說,Prompt 是給它的「指令」或「菜單」,而 Plugin function 則是額外的「廚具」或「配料」。當這些指令和功能互動時,可能會出現意料之外的結果。所以,追蹤管理 Prompt 和 Plugin function 調用,能幫助我們了解以下幾個問題:
記錄與分析 Prompt 渲染的真實情況
每次都記錄 Prompt 的完整內容,包含system prompt 、user prompt、assistant prompt,可以更好的理解與LLM互動的過程。
Plugin function 調用的細節掌控
Plugin function 是讓 LLM 更聰明的秘技,但若是用得不對,可能反而會讓模型變得「瞎忙」。透過追蹤每個 Plugin function 調用的狀況,可以知道具體執行了什麼動作,甚至可以優化這些功能的使用頻率或順序,讓模型表現更穩定。
優化資源使用
調用各種 Plugin function 或是生成各式各樣的 Prompt,都是需要資源的。隨著應用規模變大,多餘、重覆或無效的調用可能會影響整個應用。透過追蹤與管理能讓我們有效控管資源,避免不必要的浪費。
Semantic Kernel 提供了 IFunctionInvocationFilter 與 IPromptRenderFilter 二個介面,做為 function invoke 與 Prompt Render 的過濾器,因此藉由實現這二個介面可以針對 function 以及 prompt 進行觀察進而實現記錄。讓我以Day 20 的範例來示範如何實現收服 Prompt 和 Plugin Function。
private sealed class MyFunctionFilter : IFunctionInvocationFilter
{
public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next)
{
Console.WriteLine($"========= MyFunctionFilter In =========");
Console.WriteLine($"Function Invoking {context.Function.Name}\n\n");
await next(context);
var metadata = context.Result?.Metadata;
if (metadata is not null)
{
Console.WriteLine($"========= MyFunctionFilter Out =========");
foreach (var item in metadata)
{
Console.WriteLine($"{item.Key}: {JsonSerializer.Serialize(item.Value)}");
}
}
Console.WriteLine($"========= MyFunctionFilter End =========\n\n");
}
}
private sealed class MyPromptFilter : IPromptRenderFilter
{
public async Task OnPromptRenderAsync(PromptRenderContext context, Func<PromptRenderContext, Task> next)
{
Console.WriteLine($"========= MyPromptFilter In =========");
Console.WriteLine($"Rendering prompt for {context.Function.Name}\n\n");
await next(context);
Console.WriteLine($"========= MyPromptFilter Out =========");
Console.WriteLine($"Rendered prompt for {System.Text.RegularExpressions.Regex.Unescape(context.RenderedPrompt)}");
Console.WriteLine($"========= MyPromptFilter End =========\n\n");
}
}
kernel.PromptRenderFilters.Add(new MyPromptFilter());
kernel.FunctionInvocationFilters.Add(new MyFunctionFilter());
========= MyFunctionFilter In =========
Function Invoking Function_6cee94ef6ead45c8a1b4463f9aee659c
========= MyPromptFilter In =========
Rendering prompt for Function_6cee94ef6ead45c8a1b4463f9aee659c
========= MyPromptFilter Out =========
Rendered prompt for Determine if the copy has been approved. If so, respond with a single word: 'yes'
History:
[
{
"Role": "Assistant",
"Name": "ReviewerAgent",
"Content": "這段文案已經相對符合標準,特別是在吸引力和主題上。然而,為了進一步提升,還是可以考慮以下幾點:
1. **增加情感共鳴**:可以描述一下在忙碌生活中,清洗工作的繁瑣如何使人感到壓力,並強調使用AI吸塵器後帶來的輕鬆感受。
2. **豐富的視覺形容詞**:在描述產品時,加入更多的視覺形容詞,以使畫面更加生動,例如描述潔淨過後的室內環境。
3. **保持短段落結構**:確保每段落的結構保持在4到5句,以增強信息的可讀性。
4. **更頻繁的使用「你」和「我」**:試著在文中使用更多的「你」和「我」,以提升個人化的感覺。
根據這些建議進一步調整文案後,將更能吸引消費者的注意並讓其印象加深。"
}
]
========= MyPromptFilter End =========
========= MyFunctionFilter Out =========
Id: "chatcmpl-AG5yAhVhBTd8J3jn8unJIqJlE7a1D"
CreatedAt: "2024-10-08T14:59:18+00:00"
SystemFingerprint: "fp_f85bea6784"
Usage: {"OutputTokens":1,"InputTokens":1020,"TotalTokens":1021}
FinishReason: "Stop"
ContentTokenLogProbabilities: []
========= MyFunctionFilter End =========
LLM 應用開發,就像是駕馭一艘太空船。需要掌控每個指令、每個工具的使用情況,才能穩定的輸出生成結果。透過 Semantic Kernel 進行 Prompt 生成和 Plugin 調用的追蹤管理,就像是讓太空船加裝了更聰明的導航系統,避免失控以及找出最佳行車路線。所以,別再讓 LLM 自由發揮!適度進行日誌和追蹤管理吧。